﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Data;

namespace Mesuro.Common {
	/*public enum BaseMetric {
		Length,
		Mass,
		Time,
		Current,
		Temperature,
		Amount,
		Intensity
	}
	public struct Metric {
		private sbyte[] powers = new sbyte[7] { 0, 0, 0, 0, 0, 0, 0 };
		public sbyte this[BaseMetric m] {
			get {
				return powers[(int)m];
			}
			set {
				powers[(int)m] = value;
			}
		}
		private static implicit operator Metric( BaseMetric m ) {
			Metric nm = new Metric();
			nm[m] = 1;
			return nm;
		}
		private static Metric operator *( Metric m1, Metric m2 ) {
			foreach (BaseMetric b in Enum.GetValues( typeof( BaseMetric ) ))
				m1[b] += m2[b];
			return m1;
		}
		private static Metric operator /( Metric m1, Metric m2 ) {
			foreach (BaseMetric b in Enum.GetValues( typeof( BaseMetric ) ))
				m1[b] -= m2[b];
			return m1;
		}
	}*/
	public class Unit {
		public readonly String Prefix, Suffix;
		internal Unit( String Prefix, String Suffix ) {
			this.Prefix = Prefix;
			this.Suffix = Suffix;
		}
	}

	[TypeConverter( typeof( AngularUnitConverter ) )]
	public class AngularUnit : Unit {
		public static readonly AngularUnit Turn = new AngularUnit( 1, "tn" );
		public static readonly AngularUnit Binary = new AngularUnit( 2, "π" );
		public static readonly AngularUnit Radian = new AngularUnit( 2 * Math.PI, "" );
		public static readonly AngularUnit Clock = new AngularUnit( 12, "o'clock" );
		public static readonly AngularUnit Degree = new AngularUnit( 360, "\u00B0" );
		public static readonly AngularUnit Gradian = new AngularUnit( 400, "gr" );
		public static readonly AngularUnit MilTrue = new AngularUnit( 6283, "MIL" );
		public static readonly AngularUnit MilNato = new AngularUnit( 6400, "MIL" );
		public static readonly AngularUnit ArcMinute = new AngularUnit( 360 * 60, "'" );
		public static readonly AngularUnit ArcSecond = new AngularUnit( 360 * 60 * 60, "\"" );

		public readonly double FullCircle;

		public AngularUnit( double FullCircle ) : this( FullCircle, "" ) { }

		public AngularUnit( double FullCircle, String Suffix ) : this( FullCircle, "", Suffix ) { }

		public AngularUnit( double FullCircle, String Prefix, String Suffix )
			: base( Prefix, Suffix ) {
			this.FullCircle = FullCircle;
		}

		public double ConvertTo( AngularUnit to, double value ) {
			return value * to.FullCircle / FullCircle;
		}

		public double ConvertFrom( AngularUnit from, double value ) {
			return value * FullCircle / from.FullCircle;
		}

		public static double Convert( double value, AngularUnit from, AngularUnit to ) {
			return value * to.FullCircle / from.FullCircle;
		}

		public override string ToString() {
			return new AngularUnitConverter().ConvertToString( this );
		}

		public String ToString( double val ) {
			return Prefix + val.ToString() + Suffix;
		}
		public String ToString( decimal val ) {
			return Prefix + val.ToString() + Suffix;
		}
		public String ToString( double val, AngularUnit SourceUnit ) {
			return Prefix + ConvertFrom( SourceUnit, val ).ToString() + Suffix;
		}
	}

	public class AngularUnitConverter : TypeConverter, IValueConverter {
		public static readonly String[] IdentNames = new String[] { "Turn", "Binary", "Radian", "Clock", "Degree", "Gradian", "MilTrue", "MilNato", "ArcMinute", "ArcSecond" };
		public static readonly String[] DisplayNames = new String[] { "Turns", "Binary Radians", "Radians", "Clock", "Degrees", "Gradians", "MIL (True)", "MIL (NATO)", "Arc Minutes", "Arc Seconds" };
		private static readonly StandardValuesCollection svc = new StandardValuesCollection( IdentNames );
		public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType ) {
			return sourceType == typeof( string ) || base.CanConvertFrom( context, sourceType );
		}
		public override object ConvertFrom( ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value ) {
			double perCirc = 0;
			switch ((string)value) {
				case "Turn": return AngularUnit.Turn;
				case "Binary": return AngularUnit.Binary;
				case "Radian": return AngularUnit.Radian;
				case "Clock": return AngularUnit.Clock;
				case "Degree": return AngularUnit.Degree;
				case "Gradian": return AngularUnit.Gradian;
				case "MilTrue": return AngularUnit.MilTrue;
				case "MilNato": return AngularUnit.MilNato;
				case "ArcMinute": return AngularUnit.ArcMinute;
				case "ArcSecond": return AngularUnit.ArcSecond;
				default:
					if (Double.TryParse( (string)value, out perCirc ))
						return new AngularUnit( perCirc );
					return null;
			}
		}
		public override bool CanConvertTo( ITypeDescriptorContext context, Type destinationType ) {
			return destinationType == typeof( string ) || base.CanConvertTo( context, destinationType );
		}
		public override object ConvertTo( ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType ) {
			if (destinationType == typeof( string )) {
				AngularUnit unit = value as AngularUnit;
				if (unit == null) return null;
				if (unit.FullCircle == AngularUnit.Turn.FullCircle) return "Turn";
				if (unit.FullCircle == AngularUnit.Binary.FullCircle) return "Binary";
				if (unit.FullCircle == AngularUnit.Radian.FullCircle) return "Radian";
				if (unit.FullCircle == AngularUnit.Clock.FullCircle) return "Clock";
				if (unit.FullCircle == AngularUnit.Degree.FullCircle) return "Degree";
				if (unit.FullCircle == AngularUnit.Gradian.FullCircle) return "Gradian";
				if (unit.FullCircle == AngularUnit.MilTrue.FullCircle) return "MilTrue";
				if (unit.FullCircle == AngularUnit.MilNato.FullCircle) return "MilNato";
				if (unit.FullCircle == AngularUnit.ArcMinute.FullCircle) return "ArcMinute";
				if (unit.FullCircle == AngularUnit.ArcSecond.FullCircle) return "ArcSecond";
				return unit.FullCircle.ToString();
			}
			return null;
		}
		public override bool GetStandardValuesSupported( ITypeDescriptorContext context ) {
			return true;
		}
		public override TypeConverter.StandardValuesCollection GetStandardValues( ITypeDescriptorContext context ) {
			return svc;
		}

		public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
			AngularUnit unit = value as AngularUnit;
			if (unit != null) {
				if (unit == AngularUnit.Turn) return DisplayNames[0];
				if (unit == AngularUnit.Binary) return DisplayNames[1];
				if (unit == AngularUnit.Radian) return DisplayNames[2];
				if (unit == AngularUnit.Clock) return DisplayNames[3];
				if (unit == AngularUnit.Degree) return DisplayNames[4];
				if (unit == AngularUnit.Gradian) return DisplayNames[5];
				if (unit == AngularUnit.MilTrue) return DisplayNames[6];
				if (unit == AngularUnit.MilNato) return DisplayNames[7];
				if (unit == AngularUnit.ArcMinute) return DisplayNames[8];
				if (unit == AngularUnit.ArcSecond) return DisplayNames[9];
			}
			return null;
		}

		public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
			String unitName = value as String;
			if (unitName != null) {
				if (unitName.Equals( DisplayNames[0] ) || unitName.Equals( IdentNames[0] )) return AngularUnit.Turn;
				if (unitName.Equals( DisplayNames[1] ) || unitName.Equals( IdentNames[1] )) return AngularUnit.Binary;
				if (unitName.Equals( DisplayNames[2] ) || unitName.Equals( IdentNames[2] )) return AngularUnit.Radian;
				if (unitName.Equals( DisplayNames[3] ) || unitName.Equals( IdentNames[3] )) return AngularUnit.Clock;
				if (unitName.Equals( DisplayNames[4] ) || unitName.Equals( IdentNames[4] )) return AngularUnit.Degree;
				if (unitName.Equals( DisplayNames[5] ) || unitName.Equals( IdentNames[5] )) return AngularUnit.Gradian;
				if (unitName.Equals( DisplayNames[6] ) || unitName.Equals( IdentNames[6] )) return AngularUnit.MilTrue;
				if (unitName.Equals( DisplayNames[7] ) || unitName.Equals( IdentNames[7] )) return AngularUnit.MilNato;
				if (unitName.Equals( DisplayNames[8] ) || unitName.Equals( IdentNames[8] )) return AngularUnit.ArcMinute;
				if (unitName.Equals( DisplayNames[9] ) || unitName.Equals( IdentNames[9] )) return AngularUnit.ArcSecond;
			}
			return null;
		}
	}
}
